
new reserved keywords in pyrope for potential future updates:

 msb,lsb
 var,let,mut,set
 comptime

 guard,assert,ensure,assume,restrict,when

 import, export, punch

 is, isnot, subset, superset

 int, string, bool, mem

--------------------------

These are some potential ideas for Pyrope v3 based on RUST/V ideas

 //NOTE: default value must be given to xx "var xx" not OK

 var xx =  // xx should not exist, xx is mutable
 let xx =  // xx should not exist, xx is immutable

 var foo.bar.xx  = // foo.bar should exist , xx should not exist. xx is mutable
 let foo.bar.xx  = // foo.bar should exist , xx should not exist. xx is immutable

 // add var foo.bar.xx  = // foo.bar may exist , xx should not exist. xx is mutable
 // add let foo.bar.xx  = // foo.bar may exist , xx should not exist. xx is immutable

 mut foo.bar.xx  = // foo.bar.xx should exist as mutable
 set foo.bar.xx  = // (only check that foo.bar.xx is not immutable) foo.bar.xx may be mutable or not exist

 mut comptime foo.bar.xx = // comptime -> the rhs/value/tuple to assign must be known at compile time
 raw comptime foo.bar.xx = // ....

 var comptime xx =  // the assignment is comptime, future updates may not be

 // Type style (same object but the object can not affect results, just a variable type

 // Default value must be given to the type "var type xt" not OK

 var type xt =  // xt is type and mutable
 let type xt =  // xt is type and immutable, type implies comptime (types must be compile time)

 var xx:xt       // xx is type xt
 var xx:xt = ... // xx is type xt with contructor values of ...

 let xx:xt = ... // immutable version

 // Tuple syntax
 foo = (comptime bar = (var xx = 3, let yy = 4), zz = 5)

 var lhs = rhs // lhs should not exist, lhs is mutable
 let lhs = rhs // lhs should not exist, lhs is immutable

 add XXX lhs1.lhs2 = rhs // lhs1 may not exist, but lhs2 should not exist

 mut lhs = rhs // lhs should exist as mutable
 XXX comptime lhs = rhs // rhs must be known at compile time

 raw lhs = rhs // no checks besides lhs is not already declared as immutable (mutable or may not exist)

2.Ranges

 1..3 is a bit confusing. Is it 1,2,3 or 1,2?

 1:2 is more verilog/python friendly and easier to understand

 1:10:2 -> (1,3,5,7,9)

3.Bit selection

 Reserve msb/lsb keywords that can be used in ranges/bit selection

 foo@3            // pick bit 3 from foo

 foo@(1,3)        // pick bits 1 and 3
 foo@(lsb:msb)    // pick all bits
 foo@(:)          // pick all bits
 foo@(1:msb)      //pick all bits but LSB
 foo@((lsb+3):(msb-2))

 foo@ == foo@(:)  // typecast, get all bits

 tmp = foo@(1..3)  // tmp.min>=0
 tmp = foo@        // tposs
 tmp = foo.sext(2) // sign extend from bit 2
 tmp = foo@sext(2) // sign extend from bit 2
 tmp = foo@zext(2) // == foo@(2)

 tmp = foo@|()     // or reduction
 tmp = foo@&()     // and reduction
 tmp = foo@^()     // xor reduction

4.A bit cleaner assert/assume/ensure

 guard  foo < 3   // Method called only if foo < 3
 assert foo == 3  // compile or runtime check
 ensure foo == 3  // compile time check
 assume foo < 3   // enforced in verif mode
 restrict foo < 4 // constrain to help in verif mode

5.Easier function type check

 fun = ||{
   ensure $ is type(foo,bar)
   ensure %.__size == 1
   ensure % is type(sum)

   %sum = $foo + $bar
 }

----
BRAIN DUMP:

iris2 f* use some

var m:my_mem(size=1K, initial="foo.json", xxxx=3);

m.read(port=0,300) ||{
  puts "One cycle later, the read is {}", $
}

m.write(port=2,addr=301+x, data=33+a)

let counter:signed constrain { ||{ ret $ is int and $.__bits<4 and $.__min>2 } }

let xxx_type:signed constrain { ||{ ret $ between 7:23 } }

a:unsigned(0:3) = b:signed(10:20) - c:xxx_type;


Bar : (a,b) constrain { ||{ ret $a.__sbits>6 and $b is Foo } }

Base types: int, string

Bool : int constrain { ||{ $ is int and $.__sbits==1} }

---

6.Explicit import keyword

 Cleaner than current lgcpp current call

 x   = import foo.potato (bar, mod3)
 tot = import another.thing

 x.bar(3,4)
 11 |> x.mod3
 tot(3)

-Maybe make punch/import consistent. The diff is that import gets only methods and compile error if multiple match

 x = import([top], "foo.potato.(bar|mod3)") // match with regex like punch
 y = punch("any_wire_named_foo")
 for i in y {
   puts("wire:{} has value:{}\n", i.__key, i);
 }

7.If allowed to have a return

 s = if cond & 1 { (odd=1, 33) } else { (odd=0, bar+3) }

8.Tuple/Type check

 is, type keyword

 a = 3
 assert a is 1030

 b = (c=3,d=4)
 assert b is (c=0,d=0)   // value does not matter
 assert b isnot (d=0,c=0)  // ordered tuple
 assert b is type(c,d)   // syntax sugar for (c=0,d=0)

 process_tree = |{
   assert $ is type(left,right,value) or type(value)
 }

9.Init for modules/directories

__reset = :{
}

10.Allow read-only definition time scope variables capture (ONLY if the
definition variable is immutable )

a = 3
let foo = 10

do_xx = :{ return foo+$0 }

res1 = do_xx(2)
assert res1 == 12

foo = 3

res2 = do_xx(2)
assert res1 == 12

11.Enums

 Current:

 color.__allow = ('red', 'green', 'blue')
 assert color is (red=0, green=1, blue=2)

 Some syntax sugar a bit Elixir style?
 Allow :foo the same as 'foo'

12.Autodoc

 Use the vow AND prp to generate doc. Use comments like v/go/doxygen and parse
the assertions/assume

13.All variables have initial value but "undefined" is valid assign
 (this was already in Pyrope 2, but not well documented)

 mut foo = undefined
 assert foo>3 // fails
 foo = 4


============================================

Why not generics?

 -In Pyrope, every method is a generic. The assert/assume/restrict constrain the generic


14.Future types

 let type txxx :int(0:)
 let type txx  :int(-10:50)

 var x:txxx
 let x:txx = 33
 mut x = 3 // preserve type

 x = (a:txx=3,b:foo=5)

 I(x is (a:txx,b:foo))
 I(x is (a:_,b:foo))
 I(x isnot (a:foo))

 int(0:22) // min:0 max:22
 int(:22)  // min:-inf, max:22
 int(3:)   // min:3 max:inf
 string
 bool

 xx = |(a:int(0:44)|{
 }

 xx(a=3) // OK
 xx(a=50) // compile error

 var x:int(0:30)=0
 I(x isnot int(0:50))
 I(!(x is  int(0:5))) // FALSE: subset
 I(!(x subset 0:5))   // FALSE: superset
 I( (x superset 0:5)) // TRUE:  superset

15.Memory API


 //----------------------
 // RAW direct flop and memory


 ff = __flop(clock=$clk, din=xx)
 // ff is the flop_q pin

 // 1 rd and 1 wr port
 mm = __mem(entries=1K, bits=30, mode=(0,1) addr=(rd_addr, wr_addr), enable=(rd_enable, wr_enable), data_in=(0,wr_data))
 mm = __mem(entries=1K, bits=30, mode.0=0, mode.1=1, addr.0=rd_addr, addr.1=wr_addr, enable.0=rd_enable, enable.1=wr_enable, data_in.1=wr_data)

 xx = mm.Q  // result from addr.1
 //----------------------

 a = mem(entries=4K, rd_ports=3, wr_ports=4, entry_bits=32, posedge=false)

 a.write(addr=20,data=30) // generic multiport API
 x = a.read(33)

 a.wr[0].write(addr=20,data=30)
 a.rd[1].read(addr=20)
 a.wr[1].write(wmask=0xF0, addr=500, data=11)

 mem = ||{
   n_ports = $rd_ports + $wr_ports

   addr_list = () // First writes, then reads
   mode_list = ()
   data_in_list ()
   for i in 0:(wr_ports-1) {
     data_in_list ++= xx.wr[i].data_in.__last_value
     addr_list    ++= xx.wr[i].addr.__last_value
     mode_list    ++= (1)
   }
   for i in 0:(rd_ports-1) {
     data_in_list ++= (0)
     addr_list    ++= xx.rd[i].addr.__last_value
     mode_list    ++= (0)
   }

   let my_clock = this.clock

   xx = __mem(clock=my_clock, entries=$entries, bits=$entry_bits, addr=addr_list, enable=enable_list, clock=clock_list, data_in=data_inst_list, mode=mode_list)
   for pid in 0:(wr_ports-1) {
     xx.wr[pid].write = ||{
       this.data_in = $data
       this.addr    = $addr
     }
   }
   for pid in 0:(rd_ports-1) {
     xx.rd[pid].read = ||{
       this.addr = $addr
       return this[".."][".."].q[pid] // xx.q[pid] // foo.bar.jojo[".."] == for.bar
     }
   }

   for pid in 0:(rd_ports-1) {
     xx.rd[pid].read_pipe = ||{
       yy = ()
       for i in $ {
         yy[i.__key] = __flop(clock=my_clock, din=i)
       }
       this.addr = $addr
       yy.addr   = __flop(clock=my_clock,$addr);
       yy.q = this[".."][".."].q[pid]
       $.__do(yy)
     }
   }

   return xx
 }

 mem.rd[0].read_pipe(addr=10,meta_1=(x=1,b=4)) ||{
   // Method called a cycle later (when read is done)
   puts("read to addr:{} has data:{} with meta.x={}\n",$.addr, $data, $meta.x);
 }

 //----------------------

 my_custom_flop = ||{

   let ff.q   = __flop(din=ff.din.__last_value, enable=ff.enable.__last_value, initial=$initial)

   ff.din     = 0 // clear din every clock cycle
   ff.enable  = false

   ff.write = ||{
     this.enable = true
     this.din = $
   }

   ff.read = ||{
     return this.q
   }

   ff.read_pipe = ||{
     xx = ()
     for i in $ {
       xx[i.__key] = __flop(din=i)
     }
     xx.din = this.q
     $.__do(xx)
   }

   return ff
 }

 ff = my_custom_flop(initial=0)

 val_q = ff.read()
 ff.write(33)

 ff.read_pipe(vv=$runtime) ||{
   val = $din // must be 33 next cycle
   // $vv == last cycle $runtime
 }

 ff.read_pipe(foo=3) ||{
   val = $din // must be 33 next cycle
   // $foo== 3
   // $vv == last cycle $runtime
 }

--------------


Allow at pyrope (not lnast) more intuitive ops. Like

If a in x {
If a not in x {

If a between 2: {
It a between 3:8 {

A = x if condition
A = (3 if cond else 5)

a = 3 if foo else 5

mut a = 3 if boo else 5 if cond // WEIRD!

mut a = match foo {true=>3,false=>5} if cond

mut a = match foo {true=>3} else 5 if 


match tup_expr {
  1,3,4 ||{ puts("1..4, not 2 it was:{}", $) }
  55 { puts("55") },
  else { puts("Something else") }
}

a = match cond { true {123} false {33} }
a = match cond { true->123, false->33 }

a = (a?3:4)
a = (if a {3} else {4}) when cond3


if cond {
  puts "hello here"
}elif cond2 {
  //
}else{
  puts("xxx")
} when disable_condition

One line:
  let a = 3 unless enable_stuff
Expands to:
  tmp = undefined
  if enable_stuff {
    tmp = 3
  }
  let a = tmp


Python style comprehension (no nested no walrus)
A = (1<<i for I in 1:10)

PYTHON:
>>> sentence = 'the rocket came back from mars'
>>> vowels = [i for i in sentence if i in 'aeiou']
>>> vowels

OPT 1:
sentence = 'the rocket came back from mars'
vowels = ()
for i in sentence {
  if i in ('a','e','i','o','u') {
    vowels = vowels ++ i
  }
}
  
OPT 2:
filter = ||{
  res = ()
  for i in $ {
    if $.__do(i) {
      res = res ++ i
    }
  }
  return res
}
let vowels = sentence |> filter ||{ $ in 'aeiou') }

OPT 3: (NOT IMPLEMENTED. Same as OPT 2, and less syntax to support)
let vowels = (i for i in sentence if i in 'aeiou')

_------
1..3 == 1:2


----------------------------------

add = (||{true}, ||{$0+$1})
add ++= (||{$0.bits==1}, ||{ my_fast_plus_1_add($0,$1) })


xxx = (1,2) |> pick(add)

A.foo  = ||{}
a.foo ++= |(a,b)|{...
A.foo ++= |((a,b) when a<b|{...

When a matching method is found, that method is called.

Diff is that the overload tracking is a variable/ordered tuple. It goes one entry at a time to decide the call.

To allow multiple case at compile time.

A.foo.__runtime_select = false
In pyrope
x = runtime A.foo(a,b) // mux of fcalls allowed
x = comptime a.foo(a,b) // default 
Runtime x = ... // Default
Comprime x = ... // Result x know at compile time can be mut or let or var...


let comptime xx = fun(args)


y = add(x,1)
// Is y combinational or flopped??
// assert y == (x+1)
// assert y == (past(x)+1)

add = ||{ $0+$1 }
padd = ||{
  %    = @res
  @res = $0+$1
}

let comb y = add(x,1) // checks that add is pure comb
assert y == x+1

let pipe y = padd(x,1) // checks that outputs are not pure comb
assert y == past(x)+1 // x is from past

assert #y1 == past(x)+1
let pipe  y2 = add(x,1)
assert #y1 == y2
let comb #y1 = add(x,1)
maybe #y1 != y2

--------------

// coverage guided assertions (lgcpp)

cover var  // any possible value of var should be tested
cover var, reset=true // include reset in coverage
cover var, range=(1..3) // cover from just 1..3
cover(var, range=(1,30,2)) // cover var just 1,2, and 30 values

--------------


for i in 1,3,4 {
}

a = 1..33 step 4
a = 1:33 step 4


---------------------

any = ||{
  for i in $ {
    if $.__do(i) {
      return true
    }
  }
  return false
}

all = ||{
  for i in $ {
     $.__do(i)
    return false unless res
  }
  return true
}

if comptime any(2,7,3) ||{$ < 3} {
}

if (1,2,3) |> all ||{$ <= 3} {
}

